/*     */ package com.zimbra.cs.service.mail;
/*     */ 
/*     */ import com.google.common.base.Joiner;
/*     */ import com.google.common.base.Splitter;
/*     */ import com.zimbra.common.account.Key.AccountBy;
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.soap.Element;
/*     */ import com.zimbra.common.soap.MailConstants;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.StringUtil;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.account.AuthToken;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.mailbox.MailItem.Type;
/*     */ import com.zimbra.cs.mailbox.Mailbox;
/*     */ import com.zimbra.cs.mailbox.MailboxManager;
/*     */ import com.zimbra.cs.mailbox.MailboxManager.FetchMode;
/*     */ import com.zimbra.cs.service.admin.AdminServiceException;
/*     */ import com.zimbra.cs.service.util.SyncToken;
/*     */ import com.zimbra.cs.servlet.continuation.ResumeContinuationListener;
/*     */ import com.zimbra.cs.session.IWaitSet;
/*     */ import com.zimbra.cs.session.WaitSetAccount;
/*     */ import com.zimbra.cs.session.WaitSetCallback;
/*     */ import com.zimbra.cs.session.WaitSetError;
/*     */ import com.zimbra.cs.session.WaitSetError.Type;
/*     */ import com.zimbra.cs.session.WaitSetMgr;
/*     */ import com.zimbra.soap.ZimbraSoapContext;
/*     */ import java.util.ArrayList;
/*     */ import java.util.EnumSet;
/*     */ import java.util.Iterator;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import java.util.Set;
/*     */ import javax.servlet.http.HttpServletRequest;
/*     */ import org.eclipse.jetty.continuation.Continuation;
/*     */ import org.eclipse.jetty.continuation.ContinuationSupport;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class WaitSetRequest
/*     */   extends MailDocumentHandler
/*     */ {
/*  72 */   private static final long DEFAULT_TIMEOUT = LC.zimbra_waitset_default_request_timeout.longValueWithinRange(1L, 86400L);
/*  73 */   private static final long MIN_TIMEOUT = LC.zimbra_waitset_min_request_timeout.longValueWithinRange(1L, 86400L);
/*  74 */   private static final long MAX_TIMEOUT = LC.zimbra_waitset_max_request_timeout.longValueWithinRange(1L, 86400L);
/*     */   
/*  76 */   private static final long DEFAULT_ADMIN_TIMEOUT = LC.zimbra_admin_waitset_default_request_timeout.longValueWithinRange(1L, 86400L);
/*  77 */   private static final long MIN_ADMIN_TIMEOUT = LC.zimbra_admin_waitset_min_request_timeout.longValueWithinRange(1L, 86400L);
/*  78 */   private static final long MAX_ADMIN_TIMEOUT = LC.zimbra_admin_waitset_max_request_timeout.longValueWithinRange(1L, 86400L);
/*     */   
/*  80 */   private static final long INITIAL_SLEEP_TIME_MILLIS = LC.zimbra_waitset_initial_sleep_time.longValueWithinRange(1L, 300000L);
/*  81 */   private static final long NODATA_SLEEP_TIME_MILLIS = LC.zimbra_waitset_nodata_sleep_time.longValueWithinRange(1L, 300000L);
/*     */   
/*     */   public static long getTimeoutMillis(Element request, boolean isAdminRequest) throws ServiceException
/*     */   {
/*     */     long to;
/*  86 */     if (!isAdminRequest) {
/*  87 */       long to = request.getAttributeLong("timeout", DEFAULT_TIMEOUT);
/*  88 */       if (to < MIN_TIMEOUT)
/*  89 */         to = MIN_TIMEOUT;
/*  90 */       if (to > MAX_TIMEOUT)
/*  91 */         to = MAX_TIMEOUT;
/*     */     } else {
/*  93 */       to = request.getAttributeLong("timeout", DEFAULT_ADMIN_TIMEOUT);
/*  94 */       if (to < MIN_ADMIN_TIMEOUT)
/*  95 */         to = MIN_ADMIN_TIMEOUT;
/*  96 */       if (to > MAX_ADMIN_TIMEOUT)
/*  97 */         to = MAX_ADMIN_TIMEOUT;
/*     */     }
/*  99 */     return to * 1000L;
/*     */   }
/*     */   
/*     */   public void preProxy(Element request, Map<String, Object> context) throws ServiceException
/*     */   {
/* 104 */     ZimbraSoapContext zsc = getZimbraSoapContext(context);
/* 105 */     boolean adminAllowed = zsc.getAuthToken().isAdmin();
/* 106 */     setProxyTimeout(getTimeoutMillis(request, adminAllowed) + 10000L);
/* 107 */     super.preProxy(request, context);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 147 */   private static final String VARS_ATTR_NAME = WaitSetRequest.class.getName() + ".vars";
/*     */   
/*     */ 
/*     */ 
/*     */   public Element handle(Element request, Map<String, Object> context)
/*     */     throws ServiceException
/*     */   {
/* 154 */     ZimbraSoapContext zsc = getZimbraSoapContext(context);
/* 155 */     boolean adminAllowed = zsc.getAuthToken().isAdmin();
/* 156 */     Element response = zsc.createElement(MailConstants.WAIT_SET_RESPONSE);
/* 157 */     return staticHandle(request, context, response, adminAllowed);
/*     */   }
/*     */   
/*     */   public static Element staticHandle(Element request, Map<String, Object> context, Element response, boolean adminAllowed) throws ServiceException {
/* 161 */     ZimbraSoapContext zsc = getZimbraSoapContext(context);
/* 162 */     HttpServletRequest servletRequest = (HttpServletRequest)context.get("servlet.request");
/*     */     
/* 164 */     String waitSetId = request.getAttribute("waitSet");
/* 165 */     String lastKnownSeqNo = request.getAttribute("seq");
/* 166 */     boolean block = request.getAttributeBool("block", false);
/*     */     
/* 168 */     Callback cb = (Callback)servletRequest.getAttribute(VARS_ATTR_NAME);
/*     */     
/* 170 */     if (cb == null) {
/* 171 */       Continuation continuation = ContinuationSupport.getContinuation(servletRequest);
/* 172 */       cb = new Callback();
/* 173 */       cb.continuationResume = new ResumeContinuationListener(continuation);
/* 174 */       servletRequest.setAttribute(VARS_ATTR_NAME, cb);
/*     */       
/* 176 */       String defInterestStr = null;
/* 177 */       if (waitSetId.startsWith("AllWaitSet-")) {
/* 178 */         WaitSetMgr.checkRightForAllAccounts(zsc);
/*     */         
/*     */ 
/* 181 */         defInterestStr = request.getAttribute("defTypes");
/* 182 */         Set<MailItem.Type> defaultInterests = parseInterestStr(defInterestStr, EnumSet.noneOf(MailItem.Type.class));
/*     */         
/* 184 */         cb.ws = WaitSetMgr.lookupOrCreateForAllAccts(zsc.getRequestedAccountId(), waitSetId, defaultInterests, lastKnownSeqNo);
/*     */       } else {
/* 186 */         cb.ws = WaitSetMgr.lookup(waitSetId);
/*     */       }
/*     */       
/* 189 */       if (cb.ws == null) {
/* 190 */         throw AdminServiceException.NO_SUCH_WAITSET(waitSetId);
/*     */       }
/* 192 */       WaitSetMgr.checkRightForOwnerAccount(cb.ws, zsc.getRequestedAccountId());
/*     */       
/* 194 */       List<WaitSetAccount> add = parseAddUpdateAccounts(zsc, request.getOptionalElement("add"), cb.ws.getDefaultInterest());
/*     */       
/* 196 */       List<WaitSetAccount> update = parseAddUpdateAccounts(zsc, request.getOptionalElement("update"), cb.ws.getDefaultInterest());
/*     */       
/* 198 */       List<String> remove = parseRemoveAccounts(zsc, request.getOptionalElement("remove"));
/*     */       
/*     */ 
/*     */ 
/* 202 */       List<Mailbox> referencedMailboxes = new ArrayList();
/* 203 */       for (WaitSetAccount acct : add) {
/*     */         try {
/* 205 */           Mailbox mbox = MailboxManager.getInstance().getMailboxByAccountId(acct.getAccountId(), MailboxManager.FetchMode.AUTOCREATE);
/* 206 */           referencedMailboxes.add(mbox);
/*     */         } catch (ServiceException e) {
/* 208 */           ZimbraLog.session.debug("Caught exception preloading mailbox for waitset", e);
/*     */         }
/*     */       }
/* 211 */       for (WaitSetAccount acct : update) {
/*     */         try {
/* 213 */           Mailbox mbox = MailboxManager.getInstance().getMailboxByAccountId(acct.getAccountId(), MailboxManager.FetchMode.AUTOCREATE);
/* 214 */           referencedMailboxes.add(mbox);
/*     */         } catch (ServiceException e) {
/* 216 */           ZimbraLog.session.debug("Caught exception preloading mailbox for waitset", e);
/*     */         }
/*     */       }
/*     */       
/*     */ 
/*     */ 
/*     */ 
/*     */       try
/*     */       {
/* 225 */         Thread.sleep(INITIAL_SLEEP_TIME_MILLIS);
/*     */       } catch (InterruptedException ex) {}
/* 227 */       cb.errors.addAll(cb.ws.removeAccounts(remove));
/* 228 */       synchronized (cb.ws) {
/* 229 */         synchronized (cb) {
/* 230 */           cb.errors.addAll(cb.ws.doWait(cb, lastKnownSeqNo, add, update));
/*     */           
/*     */ 
/* 233 */           if (cb.completed) {
/* 234 */             block = false;
/*     */           }
/*     */         }
/*     */       }
/* 238 */       if (block)
/*     */       {
/*     */ 
/*     */         try
/*     */         {
/* 243 */           Thread.sleep(NODATA_SLEEP_TIME_MILLIS);
/*     */         } catch (InterruptedException ex) {}
/* 245 */         synchronized (cb) {
/* 246 */           if (!cb.completed) {
/* 247 */             long timeout = getTimeoutMillis(request, adminAllowed);
/* 248 */             if (ZimbraLog.soap.isTraceEnabled())
/* 249 */               ZimbraLog.soap.trace("Suspending <WaitSetRequest> for %dms", new Object[] { Long.valueOf(timeout) });
/* 250 */             cb.continuationResume.suspendAndUndispatch(timeout);
/*     */           }
/*     */         }
/*     */       }
/*     */     }
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 260 */     cb.ws.doneWaiting();
/*     */     
/* 262 */     response.addAttribute("waitSet", waitSetId);
/* 263 */     if (cb.canceled) {
/* 264 */       response.addAttribute("canceled", true);
/* 265 */     } else if (cb.completed) {
/* 266 */       response.addAttribute("seq", cb.seqNo);
/*     */       
/* 268 */       for (String s : cb.signalledAccounts) {
/* 269 */         Element saElt = response.addElement("a");
/* 270 */         saElt.addAttribute("id", s);
/*     */       }
/*     */     }
/*     */     else {
/* 274 */       response.addAttribute("seq", lastKnownSeqNo);
/*     */     }
/*     */     
/* 277 */     encodeErrors(response, cb.errors);
/*     */     
/* 279 */     return response;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   static List<WaitSetAccount> parseAddUpdateAccounts(ZimbraSoapContext zsc, Element elt, Set<MailItem.Type> defaultInterest)
/*     */     throws ServiceException
/*     */   {
/* 287 */     List<WaitSetAccount> toRet = new ArrayList();
/* 288 */     Iterator<Element> iter; if (elt != null) {
/* 289 */       for (iter = elt.elementIterator("a"); iter.hasNext();) {
/* 290 */         Element a = (Element)iter.next();
/*     */         
/* 292 */         String name = a.getAttribute("name", null);
/* 293 */         String id; if (name != null) {
/* 294 */           Account acct = Provisioning.getInstance().get(Key.AccountBy.name, name);
/* 295 */           String id; if (acct != null) {
/* 296 */             id = acct.getId();
/*     */           } else {
/* 298 */             WaitSetError err = new WaitSetError(name, WaitSetError.Type.NO_SUCH_ACCOUNT);
/* 299 */             continue;
/*     */           }
/*     */           String id;
/* 302 */         } else { id = a.getAttribute("id");
/*     */         }
/*     */         
/* 305 */         WaitSetMgr.checkRightForAdditionalAccount(id, zsc);
/*     */         
/* 307 */         String tokenStr = a.getAttribute("token", null);
/* 308 */         SyncToken token = tokenStr != null ? new SyncToken(tokenStr) : null;
/* 309 */         Set<MailItem.Type> interests = parseInterestStr(a.getAttribute("types", null), defaultInterest);
/* 310 */         toRet.add(new WaitSetAccount(id, token, interests));
/*     */       }
/*     */     }
/*     */     
/* 314 */     return toRet;
/*     */   }
/*     */   
/*     */   static List<String> parseRemoveAccounts(ZimbraSoapContext zsc, Element elt) throws ServiceException {
/* 318 */     List<String> remove = new ArrayList();
/* 319 */     Iterator<Element> iter; if (elt != null) {
/* 320 */       for (iter = elt.elementIterator("a"); iter.hasNext();) {
/* 321 */         Element a = (Element)iter.next();
/* 322 */         String id = a.getAttribute("id");
/* 323 */         WaitSetMgr.checkRightForAdditionalAccount(id, zsc);
/* 324 */         remove.add(id);
/*     */       }
/*     */     }
/* 327 */     return remove;
/*     */   }
/*     */   
/*     */   public static class Callback implements WaitSetCallback
/*     */   {
/*     */     public void dataReady(IWaitSet ws, String seqNo, boolean canceled, List<WaitSetError> inErrors, String[] signalledAccounts) {
/* 333 */       boolean trace = ZimbraLog.session.isTraceEnabled();
/* 334 */       if (trace) {
/* 335 */         String accts = signalledAccounts != null ? "[" + StringUtil.join(", ", signalledAccounts) + "]" : "<null>";
/* 336 */         ZimbraLog.session.trace("WaitSetRequest.Callback.dataReady: ws=" + ws.getWaitSetId() + ", seq=" + seqNo + (canceled ? ", CANCEL" : "") + ", accounts=" + accts);
/*     */       }
/*     */       
/* 339 */       synchronized (this) {
/* 340 */         ZimbraLog.session.debug("WaitSet: Called WaitSetCallback.dataReady()!");
/* 341 */         if ((inErrors != null) && (inErrors.size() > 0))
/* 342 */           this.errors.addAll(inErrors);
/* 343 */         this.waitSet = ws;
/* 344 */         this.canceled = canceled;
/* 345 */         this.signalledAccounts = signalledAccounts;
/* 346 */         this.seqNo = seqNo;
/* 347 */         this.completed = true;
/* 348 */         if (this.continuationResume != null) {
/* 349 */           if (trace) ZimbraLog.session.trace("WaitSetRequest.Callback.dataReady 1");
/* 350 */           this.continuationResume.resumeIfSuspended();
/* 351 */           if (trace) ZimbraLog.session.trace("WaitSetRequest.Callback.dataReady 2");
/*     */         }
/*     */       }
/* 354 */       if (trace) ZimbraLog.session.trace("WaitSetRequest.Callback.dataReady done");
/*     */     }
/*     */     
/* 357 */     public boolean completed = false;
/*     */     public boolean canceled;
/*     */     public String[] signalledAccounts;
/*     */     public IWaitSet waitSet;
/*     */     public String seqNo;
/*     */     public IWaitSet ws;
/* 363 */     public List<WaitSetError> errors = new ArrayList();
/*     */     public ResumeContinuationListener continuationResume;
/*     */   }
/*     */   
/*     */   public static enum TypeEnum {
/* 368 */     f(EnumSet.of(MailItem.Type.FOLDER)), 
/* 369 */     m(EnumSet.of(MailItem.Type.MESSAGE)), 
/* 370 */     c(EnumSet.of(MailItem.Type.CONTACT)), 
/* 371 */     a(EnumSet.of(MailItem.Type.APPOINTMENT)), 
/* 372 */     t(EnumSet.of(MailItem.Type.TASK)), 
/* 373 */     d(EnumSet.of(MailItem.Type.DOCUMENT)), 
/* 374 */     all(EnumSet.of(MailItem.Type.FOLDER, new MailItem.Type[] { MailItem.Type.MESSAGE, MailItem.Type.CONTACT, MailItem.Type.APPOINTMENT, MailItem.Type.TASK, MailItem.Type.DOCUMENT }));
/*     */     
/*     */     private final Set<MailItem.Type> types;
/*     */     
/*     */     private TypeEnum(Set<MailItem.Type> types)
/*     */     {
/* 380 */       this.types = types;
/*     */     }
/*     */     
/*     */     public Set<MailItem.Type> getTypes() {
/* 384 */       return this.types;
/*     */     }
/*     */   }
/*     */   
/*     */   public static final String expandInterestStr(Set<MailItem.Type> interest) {
/* 389 */     if (interest.containsAll(TypeEnum.all.getTypes())) {
/* 390 */       return TypeEnum.all.name();
/*     */     }
/*     */     
/* 393 */     StringBuilder result = new StringBuilder();
/* 394 */     for (MailItem.Type type : interest) {
/* 395 */       switch (type) {
/*     */       case FOLDER: 
/* 397 */         result.append(TypeEnum.f.name());
/* 398 */         break;
/*     */       case MESSAGE: 
/* 400 */         result.append(TypeEnum.m.name());
/* 401 */         break;
/*     */       case CONTACT: 
/* 403 */         result.append(TypeEnum.c.name());
/* 404 */         break;
/*     */       case APPOINTMENT: 
/* 406 */         result.append(TypeEnum.a.name());
/* 407 */         break;
/*     */       case TASK: 
/* 409 */         result.append(TypeEnum.t.name());
/* 410 */         break;
/*     */       case DOCUMENT: 
/* 412 */         result.append(TypeEnum.d.name());
/*     */       }
/*     */       
/*     */     }
/* 416 */     return result.toString();
/*     */   }
/*     */   
/*     */   public static final void encodeErrors(Element parent, List<WaitSetError> errors) {
/* 420 */     for (WaitSetError error : errors) {
/* 421 */       Element errorElt = parent.addElement("error");
/* 422 */       errorElt.addAttribute("id", error.accountId);
/* 423 */       errorElt.addAttribute("t", error.error.name());
/*     */     }
/*     */   }
/*     */   
/*     */   public static final Set<MailItem.Type> parseInterestStr(String typesList, Set<MailItem.Type> defaultInterest) {
/* 428 */     if (typesList == null) {
/* 429 */       return defaultInterest;
/*     */     }
/*     */     
/* 432 */     EnumSet<MailItem.Type> result = EnumSet.noneOf(MailItem.Type.class);
/* 433 */     for (String s : Splitter.on(',').trimResults().split(typesList)) {
/* 434 */       TypeEnum te = TypeEnum.valueOf(s);
/* 435 */       result.addAll(te.getTypes());
/*     */     }
/* 437 */     return result;
/*     */   }
/*     */   
/*     */   public static final String interestToStr(Set<MailItem.Type> interests) {
/* 441 */     EnumSet<TypeEnum> result = EnumSet.noneOf(TypeEnum.class);
/* 442 */     for (MailItem.Type type : interests) {
/* 443 */       switch (type) {
/*     */       case FOLDER: 
/* 445 */         result.add(TypeEnum.f);
/* 446 */         break;
/*     */       case MESSAGE: 
/* 448 */         result.add(TypeEnum.m);
/* 449 */         break;
/*     */       case CONTACT: 
/* 451 */         result.add(TypeEnum.c);
/* 452 */         break;
/*     */       case APPOINTMENT: 
/* 454 */         result.add(TypeEnum.a);
/* 455 */         break;
/*     */       case TASK: 
/* 457 */         result.add(TypeEnum.t);
/* 458 */         break;
/*     */       case DOCUMENT: 
/* 460 */         result.add(TypeEnum.d);
/*     */       }
/*     */       
/*     */     }
/* 464 */     return Joiner.on(',').join(result);
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/service/mail/WaitSetRequest.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */